﻿namespace HZY.Framework.Repository.EntityFramework.Interceptor;

/// <summary>
/// FreeSql审计拦击器
/// </summary>
public abstract class AbstractFreeSqlAuditAop
{
    /// <summary>
    /// 获取当前用户
    /// </summary>
    /// <returns></returns>
    protected abstract string? GetCurrentUserId();

    /// <summary>
    /// 获取雪花 id
    /// </summary>
    /// <returns></returns>
    protected abstract long GetSnowflakeId();

    /// <summary>
    /// 审计
    /// </summary>
    /// <param name="auditInfo"></param>
    /// <param name="s"></param>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public virtual void OnAudit(AuditValueEventArgs auditInfo, object? s)
    {
        var auditOptions = RepositoryEntityFrameworkExtensions.AuditOptions;
        var userId = GetCurrentUserId();

        switch (auditInfo.AuditValueType)
        {
            case AuditValueType.Insert:
                {
                    this.HandleTableId(auditInfo);

                    foreach (var audit in auditOptions)
                    {
                        var timeFieldName = audit.CreationTimeFieldName;
                        var userIdFieldName = audit.CreatorUserIdFieldName;
                        HandleEntityEntryTime(auditInfo, timeFieldName);
                        HandleEntityEntryUserId(auditInfo, userIdFieldName, userId);
                    }

                    break;
                }
            case AuditValueType.Update:
            case AuditValueType.InsertOrUpdate:
                {
                    foreach (var audit in auditOptions)
                    {
                        var timeFieldName = audit.LastModificationTimeFieldName;
                        var userIdFieldName = audit.LastModifierUserIdFieldName;
                        HandleEntityEntryTime(auditInfo, timeFieldName);
                        HandleEntityEntryUserId(auditInfo, userIdFieldName, userId);

                        // 如果是软删除
                        if (auditInfo.Property.Name == audit.DeletionTimeFieldName ||
                            auditInfo.Property.Name == audit.DeleterUserIdFieldName)
                        {
                            timeFieldName = audit.DeletionTimeFieldName;
                            userIdFieldName = audit.DeleterUserIdFieldName;
                            HandleEntityEntryTime(auditInfo, timeFieldName);
                            HandleEntityEntryUserId(auditInfo, userIdFieldName, userId);
                        }
                    }

                    break;
                }
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    /// <summary>
    /// 处理时间字段
    /// </summary>
    /// <param name="auditInfo"></param>
    /// <param name="timeFieldName"></param>
    protected virtual void HandleEntityEntryTime(AuditValueEventArgs auditInfo, string? timeFieldName)
    {
        if (auditInfo.Property.Name != timeFieldName) return;

        // 查找对象是否包含某个字段
        if (auditInfo.AuditValueType == AuditValueType.Insert)
        {
            // 创建时间
            auditInfo.Value = (DateTime?)auditInfo.Value > DateTime.MinValue
                ? auditInfo.Value
                : DateTime.Now;
        }
        else
        {
            auditInfo.Value = DateTime.Now;
        }
    }

    /// <summary>
    /// 处理操作人字段
    /// </summary>
    /// <param name="auditInfo"></param>
    /// <param name="userIdFieldName"></param>
    /// <param name="currentUserId"></param>
    protected virtual void HandleEntityEntryUserId(AuditValueEventArgs auditInfo, string? userIdFieldName, object? currentUserId)
    {
        if (auditInfo.Property.Name != userIdFieldName) return;

        // 创建人
        // 查找对象是否包含某个字段
        // userIdPropertyEntry
        if (auditInfo.Property.PropertyType == typeof(Guid) || auditInfo.Property.PropertyType == typeof(Guid?))
        {
            Guid.TryParse(currentUserId?.ToString(), out var guid);
            auditInfo.Value = auditInfo.Value ?? (guid == Guid.Empty ? null : guid);
        }
        else if (auditInfo.Property.PropertyType == typeof(long) || auditInfo.Property.PropertyType == typeof(long?))
        {
            long.TryParse(currentUserId?.ToString(), out var value);
            auditInfo.Value = auditInfo.Value ?? (value < 1 ? null : value);
        }
        else if (auditInfo.Property.PropertyType == typeof(int) || auditInfo.Property.PropertyType == typeof(int?))
        {
            int.TryParse(currentUserId?.ToString(), out var value);
            auditInfo.Value = auditInfo.Value ?? (value < 1 ? null : value);
        }
        else if (auditInfo.Property.PropertyType == typeof(string))
        {
            var value = currentUserId?.ToString();
            auditInfo.Value = auditInfo.Value ?? (string.IsNullOrWhiteSpace(value) ? null : value);
        }
        else
        {
            auditInfo.Value = currentUserId;
        }
    }

    /// <summary>
    /// 处理 表 主键 id
    /// </summary>
    /// <param name="auditInfo"></param>
    protected virtual void HandleTableId(AuditValueEventArgs auditInfo)
    {
        var keyAttribute = auditInfo.Property.GetCustomAttribute<KeyAttribute>();
        if (keyAttribute is null) return;

        var tableIdAttribute = auditInfo.Property.GetCustomAttribute<TableIdAttribute>();
        if (tableIdAttribute is null) return;

        if (this.IsNullable(auditInfo.Property.PropertyType) && auditInfo.Value != null)
        {
            return;
        }

        if (tableIdAttribute!.IdType == IdType.SnowflakeId)
        {
            long.TryParse(auditInfo.Value?.ToString(), out var result);
            if (result > 0) return;

            // 判断  propertyEntry.CurrentValue 是否为 long 类型
            if (auditInfo.Property.PropertyType == typeof(long) || auditInfo.Property.PropertyType == typeof(long?))
            {
                auditInfo.Value = GetSnowflakeId();
            }

            if (auditInfo.Property.PropertyType == typeof(string))
            {
                auditInfo.Value = GetSnowflakeId().ToString();
            }

            return;
        }

        // uuid / guid
        if (tableIdAttribute!.IdType == IdType.UuId)
        {
            if (auditInfo.Property.PropertyType == typeof(Guid) || auditInfo.Property.PropertyType == typeof(Guid?))
            {
                Guid.TryParse(auditInfo.Value?.ToString(), out var guid);
                if (guid != Guid.Empty) return;
            }

            auditInfo.Value = Guid.NewGuid();
            return;
        }

        // uuid string
        if (tableIdAttribute!.IdType != IdType.UuIdString) return;
        auditInfo.Value = Guid.NewGuid().ToString().Replace("-", "");

    }

    protected virtual bool IsNullable(Type type)
    {
        return Nullable.GetUnderlyingType(type) != null;
    }

}
